using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Web;
using System.Web.Mail;
using System.Web.Services;
using System.Collections;
using System.Web.Services.Description;
using System.Web.Services.Protocols;
using System.Xml.Serialization;
using newtelligence.DasBlog.Runtime;
using newtelligence.DasBlog.Web.Core;
using newtelligence.DasBlog.Web.Services.Rsd;

namespace newtelligence.DasBlog.Web.Services
{
	/// <summary>
	/// Summary description for DasBlogEditting.
	/// </summary>
	public class EditServiceImplementation : WebService
	{
		public EditServiceImplementation()
		{
			InitializeComponent();
		}

		#region Component Designer generated code

		//Required by the Web Services Designer 
		private IContainer components = null;

		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose(bool disposing)
		{
			if (disposing && components != null)
			{
				components.Dispose();
			}
			base.Dispose(disposing);
		}

		#endregion

		[WebMethod]
		public bool CanEdit(string username, string password)
		{
			if (!SiteConfig.GetSiteConfig().EnableEditService)
			{
				throw new ServiceDisabledException();
			}

			try
			{
				if (HttpContext.Current.Request.IsAuthenticated)
				{
					UserToken token = SiteSecurity.GetToken(User.Identity.Name);
					if (token.Role == "admin") return true;
				}

				return SiteSecurity.Login(username, password).Role == "admin";
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
				return false;
			}
		}

		[WebMethod]
		[SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
		[return:XmlAnyElement]
		public RsdRoot GetRsd()
		{
			SiteConfig siteConfig = SiteConfig.GetSiteConfig();
			RsdApiCollection apiCollection = new RsdApiCollection();
			
			RsdRoot rsd = new RsdRoot();
			RsdService dasBlogService = new RsdService();
			dasBlogService.HomePageLink = Utils.GetBaseUrl(siteConfig);
			RsdApi metaWeblog = new RsdApi();
			metaWeblog.Name = "MetaWeblog";
			metaWeblog.Preferred = true;
			metaWeblog.ApiLink = Utils.GetBloggerUrl(siteConfig);
			metaWeblog.BlogID = dasBlogService.HomePageLink;
			apiCollection.Add(metaWeblog);
			
//			RsdApi blogger = new RsdApi();
//			blogger.Name = "Blogger";
//			blogger.Preferred = false;
//			blogger.ApiLink = Utils.GetBloggerUrl(siteConfig);
//			blogger.BlogID = "";
//			apiCollection.Add(blogger);

			dasBlogService.RsdApiCollection = apiCollection;
			rsd.Services.Add(dasBlogService);
			
			
			return rsd;
		}
		
		[WebMethod]
		public void DeleteEntry(string entryId, string username, string password)
		{
			SiteConfig siteConfig = SiteConfig.GetSiteConfig();
			if (!siteConfig.EnableEditService)
			{
				throw new ServiceDisabledException();
			}

			if (SiteSecurity.Login(username, password).Role != "admin")
			{
				throw new Exception("Invalid Password");
			}

			ILoggingDataService logService = LoggingDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.LogDir));
			IBlogDataService dataService = BlogDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.ContentDir), logService);

			//dataService.DeleteEntry(entryId, siteConfig.CrosspostSites);
			DeleteEntry(entryId, siteConfig, logService, dataService);
		}

		[WebMethod]
		public string UpdateEntry(Entry entry, string username, string password)
		{
			SiteConfig siteConfig = SiteConfig.GetSiteConfig();
			if (!siteConfig.EnableEditService)
			{
				throw new ServiceDisabledException();
			}

			if (SiteSecurity.Login(username, password).Role != "admin")
			{
				throw new Exception("Invalid Password");
			}

			ILoggingDataService logService = LoggingDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.LogDir));
			IBlogDataService dataService = BlogDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.ContentDir), logService);

			UpdateEntry(entry, "", null, siteConfig, logService, dataService);

			return "not found";
		}

		[WebMethod]
		public string CreateEntry(Entry entry, string username, string password)
		{
			SiteConfig siteConfig = SiteConfig.GetSiteConfig();
			if (!siteConfig.EnableEditService)
			{
				throw new ServiceDisabledException();
			}

			if (SiteSecurity.Login(username, password).Role != "admin")
			{
				throw new Exception("Invalid Password");
			}

			// ensure that the entryId was filled in
			//
			if (entry.EntryId == null || entry.EntryId.Length == 0)
			{
				entry.EntryId = Guid.NewGuid().ToString();
			}

			// ensure the dates were filled in, otherwise use NOW
			if (entry.CreatedUtc == DateTime.MinValue)
			{
				entry.CreatedUtc = DateTime.UtcNow;
			}
			if (entry.ModifiedUtc == DateTime.MinValue)
			{
				entry.ModifiedUtc = DateTime.UtcNow;
			}

			ILoggingDataService logService = LoggingDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.LogDir));
			IBlogDataService dataService = BlogDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.ContentDir), logService);

			SaveEntry(entry, "", null, siteConfig, logService, dataService);

			return entry.EntryId;
		}

		public static void DeleteEntry(string entryId, SiteConfig siteConfig, ILoggingDataService logService, IBlogDataService dataService)
		{
			try
			{
				Entry entry = dataService.GetEntry(entryId);
				string permalink = Utils.GetPermaLinkUrl(entry.EntryId);
				//string[] categories = entry.GetSplitCategories();

				dataService.DeleteEntry(entryId, siteConfig.CrosspostSites);

				BreakCache(siteConfig, entry.GetSplitCategories());

				// give the XSS upstreamer a hint that things have changed
				XSSUpstreamer.TriggerUpstreaming();

				// TODO: when we add support for more than just enclosures, we can't delete the entire folder
				DirectoryInfo enclosuresPath = new DirectoryInfo((Path.Combine(SiteConfig.GetBinariesPathFromCurrentContext(), entryId)));
				if (enclosuresPath.Exists) enclosuresPath.Delete(true);


				logService.AddEvent(
					new EventDataItem(
					EventCodes.EntryDeleted, entry.Title,
					permalink));
			}
			catch (Exception ex)
			{
				StackTrace st = new StackTrace();
				logService.AddEvent(
					new EventDataItem(EventCodes.Error, ex.ToString() + Environment.NewLine + st.ToString(), HttpContext.Current.Request.RawUrl));
			}
		}

		public static void SaveEntry(Entry entry, SiteConfig siteConfig, ILoggingDataService logService, IBlogDataService dataService)
		{
			SaveEntry(entry, null, null, siteConfig, logService, dataService);
		}

		public static void SaveEntry(Entry entry, CrosspostInfoCollection crosspostList, SiteConfig siteConfig, ILoggingDataService logService, IBlogDataService dataService)
		{
			SaveEntry(entry, null, crosspostList, siteConfig, logService, dataService);
		}

		public static void SaveEntry(Entry entry, string trackBackText, CrosspostInfoCollection crosspostList, SiteConfig siteConfig, ILoggingDataService logService, IBlogDataService dataService)
		{
			InternalSaveEntry(entry, trackBackText, crosspostList, siteConfig, logService, dataService);

			logService.AddEvent(
				new EventDataItem(
					EventCodes.EntryAdded, entry.Title,
					Utils.GetPermaLinkUrl( siteConfig, entry.EntryId)));
		}

		public static void UpdateEntry(Entry entry, string trackBackText, CrosspostInfoCollection crosspostList, SiteConfig siteConfig, ILoggingDataService logService, IBlogDataService dataService)
		{
			InternalSaveEntry(entry, trackBackText, crosspostList, siteConfig, logService, dataService);

			logService.AddEvent(
				new EventDataItem(
					EventCodes.EntryChanged, entry.Title,
					Utils.GetPermaLinkUrl(entry.EntryId)));
		}

		private static void InternalSaveEntry(Entry entry, string trackBackText, CrosspostInfoCollection crosspostList, SiteConfig siteConfig, ILoggingDataService logService, IBlogDataService dataService)
		{
			// we want to prepopulate the cross post collection with the crosspost footer
			if (siteConfig.EnableCrossPostFooter && siteConfig.CrossPostFooter != null && siteConfig.CrossPostFooter.Length > 0)
			{
				foreach (CrosspostInfo info in crosspostList)
				{
					info.CrossPostFooter = siteConfig.CrossPostFooter;
				}
			}

			// now save the entry, passign in all the necessary Trackback and Pingback info.
			try
			{
				// if the post is missing a title don't publish it
				if (entry.Title == null || entry.Title.Length == 0)
				{
					entry.IsPublic = false;
				}

				dataService.SaveEntry(
					entry,
					(siteConfig.PingServices.Count > 0) ?
						new WeblogUpdatePingInfo(siteConfig.Title, Utils.GetBaseUrl(siteConfig), Utils.GetBaseUrl(siteConfig), Utils.GetRssUrl(siteConfig), siteConfig.PingServices) : null,
					(trackBackText != null && trackBackText.Length > 0 && entry.IsPublic) ?
						new TrackbackInfo(
							trackBackText,
							Utils.GetPermaLinkUrl( siteConfig, entry ),
							entry.Title,
							entry.Description,
							siteConfig.Title) : null,
					siteConfig.EnableAutoPingback && entry.IsPublic ?
						new PingbackInfo(
							Utils.GetPermaLinkUrl( siteConfig, entry ),
							entry.Title,
							entry.Description,
							siteConfig.Title) : null,
					crosspostList);

				SendEmail(entry, siteConfig, logService);
			}
			catch (Exception ex)
			{
				StackTrace st = new StackTrace();
				logService.AddEvent(
					new EventDataItem(EventCodes.Error, ex.ToString() + Environment.NewLine + st.ToString(), ""));
			}

			// we want to invalidate all the caches so users get the new post
			BreakCache(siteConfig, entry.GetSplitCategories());

			// give the XSS upstreamer a hint that things have changed
			XSSUpstreamer.TriggerUpstreaming();
		}

		private static void BreakCache(SiteConfig siteConfig, string[] categories)
		{
			// break the caching
			HttpRuntime.Cache.Remove("BlogCoreData");
			HttpRuntime.Cache.Remove("Rss::" + siteConfig.RssDayCount.ToString() + ":" + siteConfig.RssEntryCount.ToString());
	
			foreach(string category in categories)
			{
				string CacheKey = "Rss:" + category + ":" + siteConfig.RssDayCount.ToString() + ":" + siteConfig.RssEntryCount.ToString();
				HttpRuntime.Cache.Remove(CacheKey);
			}
		}

		/// <summary>
		/// Send an email notification that an entry has been made.
		/// </summary>
		/// <param name="siteConfig">The page making the request.</param>
		/// <param name="entry">The entry being added.</param>
		internal static void SendEmail(Entry entry, SiteConfig siteConfig, ILoggingDataService logService)
		{
			if (siteConfig.SendPostsByEmail &&
				siteConfig.SmtpServer != null &&
				siteConfig.SmtpServer.Length > 0)
			{
				ArrayList actions = new ArrayList();
				actions.Add(ComposeMail(entry, siteConfig));
						
				foreach(User user in SiteSecurity.GetSecurity().Users)
				{
					if (user.EmailAddress == null || user.EmailAddress.Length == 0)
						continue;

					if (user.NotifyOnNewPost)
					{
						SendMailInfo sendMailInfo = ComposeMail(entry, siteConfig);
						sendMailInfo.Message.To = user.EmailAddress;
						actions.Add(sendMailInfo);
					}
				}

				IBlogDataService dataService = BlogDataServiceFactory.GetService(HttpContext.Current.Server.MapPath(siteConfig.ContentDir), logService);
				dataService.RunActions((object[])actions.ToArray(typeof(object)));		
			}
		}
		
		private static SendMailInfo ComposeMail(Entry entry, SiteConfig siteConfig)
		{
				MailMessage emailMessage = new MailMessage();

				if (siteConfig.NotificationEMailAddress != null &&
					siteConfig.NotificationEMailAddress.Length > 0)
				{
					emailMessage.To = siteConfig.NotificationEMailAddress;
				}
				else
				{
					emailMessage.To = siteConfig.Contact;
				}

				emailMessage.Subject = String.Format("<p>Weblog post by '{0}' on '{1}'</p>", entry.Author, entry.Title);
				emailMessage.Body = String.Format("{0}<p/>Post page: <a href=\"{1}\">{1}</a>", entry.Content, Utils.GetPermaLinkUrl(siteConfig,entry));
				emailMessage.BodyFormat = MailFormat.Html;
				emailMessage.BodyEncoding = Encoding.UTF8;

				User entryAuthor = SiteSecurity.GetUser(entry.Author);

				//didn't work? try again with emailAddress...
				if (entryAuthor == null) 
				{
					entryAuthor = SiteSecurity.GetUserByEmail(entry.Author);
				}

				if (entryAuthor != null && entryAuthor.EmailAddress != null && entryAuthor.EmailAddress.Length > 0)
				{
					emailMessage.From = entryAuthor.EmailAddress;
				}
				else
				{
					emailMessage.From = siteConfig.Contact;
				}

				SendMailInfo sendMailInfo = new SendMailInfo(emailMessage, siteConfig.SmtpServer,
                    siteConfig.EnableSmtpAuthentication, siteConfig.SmtpUserName, siteConfig.SmtpPassword, siteConfig.SmtpPort);

			// Christoph De Baene: Put in comments, because mail is sent through IBlogDataService where the EnableSmtpAuthentication is handled there.
			// RyanG: Added support for authentication on outgoing smtp	
			/*			
			if (sendMailInfo.EnableSmtpAuthentication) 
			{
				sendMailInfo.Message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", "1");	// enable basic authentication
				sendMailInfo.Message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", sendMailInfo.SmtpUserName);	// set the username
				sendMailInfo.Message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", sendMailInfo.SmtpPassword);  // set the password
			}
			*/

			return sendMailInfo;				
		}
	}
}
